﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using jade.core;
using jade.core.behaviours;
using jade.lang.acl;
using Newtonsoft.Json;

namespace CMS1 {
    public class Reporter : Agent {
        public const int COMPLETE_STATE_DETAILS = 4000;
        public const int SUBMISSION_WITHDRAW = 4001;
        public const int GENERATE_REPORT = 4002;
        public const int SUBMISSION_REVIEW = 4003;

        //public const int PROCESS_WITHDRAW = 4001;//"WITHDRAW";

        protected DatabaseWrapper db;

        public override void setup() {
            db = new DatabaseWrapper();
            db.initConnection(DatabaseWrapper.connString);
        }

        private void requestStateDetails(string agentName, int reportType, string data) {
            ACLMessage aclMessage = new ACLMessage(ACLMessage.REQUEST);
            aclMessage.addReceiver(new AID(agentName, AID.ISLOCALNAME));
            Message msg = new Message();
            msg.Purpose = reportType;
            msg.Data = data;
            aclMessage.setContent(JsonConvert.SerializeObject(msg));
            send(aclMessage);
        }
        /*************************************************
         * Receive External
         * **********************************************/
        class ReceiveExternalMsgs : CyclicBehaviour {
            MessageTemplate externalMsg;
            public ReceiveExternalMsgs() {
                externalMsg = MessageTemplate.MatchSender(new AID("GatewayAgent", AID.ISLOCALNAME));
            }
            public override void action() {
                ACLMessage aclMsg = myAgent.receive(externalMsg);
                if (aclMsg != null) {
                    Message msg = JsonConvert.DeserializeObject<Message>(aclMsg.getContent());
                    switch (msg.Purpose) {
                        case SUBMISSION_WITHDRAW:
                            string submissionDetails = JsonConvert.DeserializeObject<string>(msg.Data);//aclMsg.getContent().Substring(5);//.Split(',');
                            informScheduler(submissionDetails);
                            break;
                        case GENERATE_REPORT:// external request to generate a report
                            int reportType = JsonConvert.DeserializeObject<int>(msg.Data);
                            switch (reportType) {
                                case Communicator.INVITATION_RESULT:
                                case Communicator.DEALING_RESULT:
                                    ((Reporter)myAgent).requestStateDetails("Communicator", reportType, "");
                                    break;
                                case Scheduler.STATE_DETAILS:
                                    ((Reporter)myAgent).requestStateDetails("Scheduler", Scheduler.STATE_DETAILS, "");
                                    break;
                                case SUBMISSION_REVIEW:
                                    break;
                            }
                            break;
                    }
                } else {
                    block();
                }
            }

            private void informScheduler(string submissionDetails) {
                ACLMessage aclMsg = new ACLMessage(ACLMessage.INFORM);
                aclMsg.addReceiver(new AID("Scheduler", AID.ISLOCALNAME));
                Message msg = new Message();
                msg.Purpose = SUBMISSION_WITHDRAW;
                msg.Data = submissionDetails;
                aclMsg.setContent(JsonConvert.SerializeObject(msg));
                myAgent.send(aclMsg);
            }
        }

        /*************************************************
         * Receive Internal
         * **********************************************/
        class ReceiveInternalMsgs : CyclicBehaviour {
            MessageTemplate internalMsg;
            DatabaseWrapper db;
            //private string[] phaseDetails;
            //private int receivedDetails;
            public ReceiveInternalMsgs(DatabaseWrapper db) {
                this.db = db;
                MessageTemplate fromScheduler = MessageTemplate.MatchSender(new AID("Scheduler", AID.ISLOCALNAME));
                MessageTemplate fromCommunicator = MessageTemplate.MatchSender(new AID("Communicator", AID.ISLOCALNAME));
                //MessageTemplate fromMonitor = MessageTemplate.MatchSender(new AID("Monitor", AID.ISLOCALNAME));
                internalMsg = MessageTemplate.or(fromScheduler, fromCommunicator);
            }
            public override void action() {
                ACLMessage aclMsg = myAgent.receive(internalMsg);
                if (aclMsg != null) {
                    Message msg = JsonConvert.DeserializeObject<Message>(aclMsg.getContent());
                    if (msg.Purpose == Scheduler.PHASE_TRANSMISSION) {
                        //AID[] agents = { new AID("Communicator", AID.ISLOCALNAME), new AID("Scheduler", AID.ISLOCALNAME) };
                        //phaseDetails = new string[agents.Length + 1];
                        string[] phaseDetails = JsonConvert.DeserializeObject<string[]>(msg.Data);
                        List<int> phaseReports = JsonConvert.DeserializeObject<List<int>>(phaseDetails[0]);
                        foreach (int report in phaseReports) {
                            switch (report) {
                                case Communicator.INVITATION_RESULT | Scheduler.PHASE_TRANSMISSION:
                                case Communicator.DEALING_RESULT | Scheduler.PHASE_TRANSMISSION:
                                    ((Reporter)myAgent).requestStateDetails("Communicator",report, phaseDetails[1]);
                                    break;
                            }
                        }
                    /*} else if (msg.Purpose == Communicator.STATE_DETAILS || msg.Purpose == Scheduler.STATE_DETAILS) {
                        generatePhaseTransmissionReport(msg.Purpose, msg.Data);*/
                    } else {
                        generateReport(msg.Purpose, msg.Data);
                    }
                } else {
                    block();
                }
            }
            private void insertReport(string content, int significance, int report_about) {
                int targetedChairID = 0;
                switch (report_about) {
                    case Communicator.INVITATION_RESULT:
                        targetedChairID = (int)db.selectCell("chair_id").from("Task").
                            where("name='Contact mass media'").and(GatewayAgent.myConf).execute();
                        break;
                    case Communicator.DEALING_RESULT:
                        targetedChairID = (int)db.selectCell("chair_id").from("Task").
                        where("name='Arrange residence'").and(GatewayAgent.myConf).execute();
                        break;
                    case Scheduler.RECEPTION_SCHEDULING:
                        targetedChairID = (int)db.selectCell("chair_id").from("Task").
                        where("name='Manage reception'").and(GatewayAgent.myConf).execute();
                        break;
                }
                db.insert_into("Report").columns("content, generate_time, significance, report_about, targeted_chair_id, conf_id").
                    values("'" + content + "',GETDATE(),'" + significance + "','" +
                     report_about + "','" + targetedChairID + "','" + GatewayAgent.CONF_ID + "'").execute();
            }


            private void generateReport(int purpose, string data) {
                switch (purpose) {
                    case Communicator.INVITATION_RESULT:
                    case Communicator.DEALING_RESULT:
                    case Communicator.INVITATION_RESULT | Scheduler.PHASE_TRANSMISSION:
                    case Communicator.DEALING_RESULT | Scheduler.PHASE_TRANSMISSION:
                        insertReport(data, 3, purpose);
                        break;
                    case Communicator.DEALING_ISSUE:
                        List<object> contents = JsonConvert.DeserializeObject<List<object>>(data);
                        insertReport((string)contents[1], (int)contents[0], purpose);
                        break;
                    /*case Scheduler.UNSOLVED_SCHEDULING_CONFLICT: // significance 1
                        break;
                    case Scheduler.WITHDRAW_PROCESSING_RESULT: // significance 2
                        break;*/
                    case Scheduler.RECEPTION_SCHEDULING:
                        insertReport(data, 3, purpose);
                        break;
                }
            }

            /*private void generatePhaseTransmissionReport(int purpose, string data) {
                if (purpose == Scheduler.STATE_DETAILS) {
                    phaseDetails[1] = data;
                    receivedDetails++;
                } else if (purpose == Communicator.STATE_DETAILS) {
                    phaseDetails[2] = data;
                    receivedDetails++;
                }
                if (receivedAllStatesDetails()) {
                    string[] phaseEncapsulation = new string[2];
                    phaseEncapsulation[0] = phaseDetails[0];
                    string serializedPhaseEncapsulation;

                    // Communicator
                    string[] communicatorStateDetails = JsonConvert.DeserializeObject<string[]>(phaseDetails[2]);
                    phaseEncapsulation[1] = communicatorStateDetails[0];
                    serializedPhaseEncapsulation = JsonConvert.SerializeObject(phaseEncapsulation);
                    insertReport(serializedPhaseEncapsulation, 3, Communicator.INVITATION_RESULT | Scheduler.PHASE_TRANSMISSION);

                    phaseEncapsulation[1] = communicatorStateDetails[1];
                    serializedPhaseEncapsulation = JsonConvert.SerializeObject(phaseEncapsulation);
                    insertReport(serializedPhaseEncapsulation, 3, Communicator.DEALING_RESULT | Scheduler.PHASE_TRANSMISSION);
                }
            }
            private bool receivedAllStatesDetails() {
                return phaseDetails.Length == receivedDetails;
            }*/

        }
    }
}